/**************************************************************
 * Copyright (c) 2021 anxinsec.com, Inc. All Rights Reserved
 * User: zhangdongsheng<zhangdongsheng@anxinsec.com>
 * Date: 2021/11/17
 * Desc:
 **************************************************************/

package utils

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"errors"
)

// AesDecrypt Aes解密
func AesDecrypt(encrypted, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
		//panic("err is:" + err.Error())
	}
	blockMode := newECBDecryptor(block)
	origData := make([]byte, len(encrypted))
	blockMode.CryptBlocks(origData, encrypted)
	return origData, nil
}

// AesEncrypt Aes加密
func AesEncrypt(src, key string) ([]byte, error) {
	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return nil, err
		//panic("key error1" + err.Error())
	}
	if src == "" {
		//panic("plain content empty")
		return nil, errors.New("plain text empty")
	}
	ecb := newECBEncryptor(block)
	content := []byte(src)
	content = pkCS5Padding(content, block.BlockSize())
	encrypted := make([]byte, len(content))
	ecb.CryptBlocks(encrypted, content)
	return encrypted, nil
}

func pkCS5Padding(ciphertext []byte, blockSize int) []byte {
	if len(ciphertext)%blockSize > 0 {
		padding := blockSize - len(ciphertext)%blockSize
		padtext := bytes.Repeat([]byte{0}, padding)
		return append(ciphertext, padtext...)
	}
	return ciphertext
}

// /////new Aes///////////
func pkCS5PaddingV2(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func pkCS5UnPaddingV2(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

// AesDecryptV2 Aes解密
func AesDecryptV2(crypted, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
		//panic("err is:" + err.Error())
	}
	blockMode := newECBDecryptor(block)
	origData := make([]byte, len(crypted))
	blockMode.CryptBlocks(origData, crypted)
	return pkCS5UnPaddingV2(origData), nil
}

// AesEncryptV2 Aes加密
func AesEncryptV2(src, key string) ([]byte, error) {
	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return nil, err
		//panic("key error1" + err.Error())
	}
	if len(src) == 0 {
		return nil, errors.New("plain text empty")
		//panic("plain content empty")
	}
	ecb := newECBEncryptor(block)
	content := pkCS5PaddingV2([]byte(src), block.BlockSize())
	encrypted := make([]byte, len(content))
	ecb.CryptBlocks(encrypted, content)
	return encrypted, nil
}

///////new Aes///////////

type ecb struct {
	b         cipher.Block
	blockSize int
}

func newECB(b cipher.Block) *ecb {
	return &ecb{
		b:         b,
		blockSize: b.BlockSize(),
	}
}

type ecbEncryptor ecb

func newECBEncryptor(b cipher.Block) cipher.BlockMode {
	return (*ecbEncryptor)(newECB(b))
}

func (x *ecbEncryptor) BlockSize() int { return x.blockSize }

func (x *ecbEncryptor) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		//panic("crypto/cipher: input not full blocks")
		return
	}
	if len(dst) < len(src) {
		//panic("crypto/cipher: output smaller than input")
		return
	}
	for len(src) > 0 {
		x.b.Encrypt(dst, src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

type ecbDecryptor ecb

func newECBDecryptor(b cipher.Block) cipher.BlockMode {
	return (*ecbDecryptor)(newECB(b))
}

func (x *ecbDecryptor) BlockSize() int { return x.blockSize }

func (x *ecbDecryptor) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		return
		//panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		return
		//panic("crypto/cipher: output smaller than input")
	}
	for len(src) > 0 {
		x.b.Decrypt(dst, src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}
